/**
 * 字符工具类
 */
export class charUtil {
    /**
     * 是否为ASCII字符，ASCII字符位于0~127之间
     *
     * @example
     * ```typescript
     * charUtil.isAscii('a')  = true
     * charUtil.isAscii('A')  = true
     * charUtil.isAscii('3')  = true
     * charUtil.isAscii('-')  = true
     * charUtil.isAscii('\n') = true
     * charUtil.isAscii('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为ASCII字符，ASCII字符位于0~127之间
     */
    static isAscii(ch: string): boolean {
        return ch.charCodeAt(0) < 128;
    }

    /**
     * 是否为可见ASCII字符，可见字符位于32~126之间
     *
     * @example
     * ```typescript
     * charUtil.isAsciiPrintable('a')  = true
     * charUtil.isAsciiPrintable('A')  = true
     * charUtil.isAsciiPrintable('3')  = true
     * charUtil.isAsciiPrintable('-')  = true
     * charUtil.isAsciiPrintable('\n') = false
     * charUtil.isAsciiPrintable('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为ASCII可见字符，可见字符位于32~126之间
     */
    static isAsciiPrintable(ch: string): boolean {
        const code = ch.charCodeAt(0);
        return code >= 32 && code < 127;
    }

    /**
     * 是否为ASCII控制符（不可见字符），控制符位于0~31和127
     *
     * @example
     * ```typescript
     * charUtil.isAsciiControl('a')  = false
     * charUtil.isAsciiControl('A')  = false
     * charUtil.isAsciiControl('3')  = false
     * charUtil.isAsciiControl('-')  = false
     * charUtil.isAsciiControl('\n') = true
     * charUtil.isAsciiControl('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为控制符，控制符位于0~31和127
     */
    static isAsciiControl(ch: string): boolean {
        const code = ch.charCodeAt(0);
        return code < 32 || code === 127;
    }

    /**
     * 判断是否为字母（包括大写字母和小写字母）
     * 字母包括A~Z和a~z
     *
     * @example
     * ```typescript
     * charUtil.isLetter('a')  = true
     * charUtil.isLetter('A')  = true
     * charUtil.isLetter('3')  = false
     * charUtil.isLetter('-')  = false
     * charUtil.isLetter('\n') = false
     * charUtil.isLetter('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为字母（包括大写字母和小写字母）字母包括A~Z和a~z
     */
    static isLetter(ch: string): boolean {
        return this.isLetterUpper(ch) || this.isLetterLower(ch);
    }

    /**
     * 判断是否为大写字母，大写字母包括A~Z
     *
     * @example
     * ```typescript
     * charUtil.isLetterUpper('a')  = false
     * charUtil.isLetterUpper('A')  = true
     * charUtil.isLetterUpper('3')  = false
     * charUtil.isLetterUpper('-')  = false
     * charUtil.isLetterUpper('\n') = false
     * charUtil.isLetterUpper('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为大写字母，大写字母包括A~Z
     */
    static isLetterUpper(ch: string): boolean {
        return ch >= 'A' && ch <= 'Z';
    }

    /**
     * 检查字符是否为小写字母，小写字母指a~z
     *
     * @example
     * ```typescript
     * charUtil.isLetterLower('a')  = true
     * charUtil.isLetterLower('A')  = false
     * charUtil.isLetterLower('3')  = false
     * charUtil.isLetterLower('-')  = false
     * charUtil.isLetterLower('\n') = false
     * charUtil.isLetterLower('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为小写字母，小写字母指a~z
     */
    static isLetterLower(ch: string): boolean {
        return ch >= 'a' && ch <= 'z';
    }

    /**
     * 检查是否为数字字符，数字字符指0~9
     *
     * @example
     * ```typescript
     * charUtil.isNumber('a')  = false
     * charUtil.isNumber('A')  = false
     * charUtil.isNumber('3')  = true
     * charUtil.isNumber('-')  = false
     * charUtil.isNumber('\n') = false
     * charUtil.isNumber('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为数字字符，数字字符指0~9
     */
    static isNumber(ch: string): boolean {
        return ch >= '0' && ch <= '9';
    }

    /**
     * 是否为16进制规范的字符，判断是否为如下字符
     * 1. 0~9
     * 2. a~f
     * 3. A~F
     *
     * @param ch 字符
     * @return 是否为16进制规范的字符
     */
    static isHexChar(ch: string): boolean {
        return this.isNumber(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
    }

    /**
     * 是否为字母或数字，包括A~Z、a~z、0~9
     *
     * @example
     * ```typescript
     * charUtil.isLetterOrNumber('a')  = true
     * charUtil.isLetterOrNumber('A')  = true
     * charUtil.isLetterOrNumber('3')  = true
     * charUtil.isLetterOrNumber('-')  = false
     * charUtil.isLetterOrNumber('\n') = false
     * charUtil.isLetterOrNumber('&copy;') = false
     * ```
     *
     * @param ch 被检查的字符
     * @return true表示为字母或数字，包括A~Z、a~z、0~9
     */
    static isLetterOrNumber(ch: string): boolean {
        return this.isLetter(ch) || this.isNumber(ch);
    }

    /**
     * 是否空白符
     * 空白符包括空格、制表符、全角空格和不间断空格
     *
     * @param ch 字符
     * @return 是否空白符
     */
    static isBlankChar(ch: string): boolean {
        const code = ch.charCodeAt(0);
        return code === 0x20 || // 空格
            code === 0x09 || // 制表符
            code === 0x3000 || // 全角空格
            code === 0xFEFF || // 不间断空格
            code === 0x202A || // 左到右嵌入
            code === 0x0000 || // 空字符
            code === 0x3164 || // Hangul Filler
            code === 0x2800 || // Braille Pattern Blank
            code === 0x200C || // Zero Width Non-Joiner
            code === 0x180E; // MONGOLIAN VOWEL SEPARATOR
    }

    /**
     * 判断是否为emoji表情符
     *
     * @param ch 字符
     * @return 是否为emoji
     */
    static isEmoji(ch: string): boolean {
        const code = ch.charCodeAt(0);
        return !(
            code === 0x0 ||
            code === 0x9 ||
            code === 0xA ||
            code === 0xD ||
            (code >= 0x20 && code <= 0xD7FF) ||
            (code >= 0xE000 && code <= 0xFFFD) ||
            (code >= 0x100000 && code <= 0x10FFFF)
        );
    }

    /**
     * 是否为Windows或者Linux（Unix）文件分隔符
     * Windows平台下分隔符为\，Linux（Unix）为/
     *
     * @param ch 字符
     * @return 是否为Windows或者Linux（Unix）文件分隔符
     */
    static isFileSeparator(ch: string): boolean {
        return ch === '/' || ch === '\\';
    }

    /**
     * 比较两个字符是否相同
     *
     * @param ch1 字符1
     * @param ch2 字符2
     * @param caseInsensitive 是否忽略大小写
     * @return 是否相同
     */
    static equals(ch1: string, ch2: string, caseInsensitive: boolean = false): boolean {
        if (caseInsensitive) {
            return ch1.toLowerCase() === ch2.toLowerCase();
        }
        return ch1 === ch2;
    }

    /**
     * 将字母、数字转换为带圈的字符
     *
     * @example
     * ```typescript
     * charUtil.toCloseChar('1') // '①'
     * charUtil.toCloseChar('A') // 'Ⓐ'
     * charUtil.toCloseChar('a') // 'ⓐ'
     * ```
     *
     * @param ch 被转换的字符，如果字符不支持转换，返回原字符
     * @return 转换后的字符
     */
    static toCloseChar(ch: string): string {
        let code = ch.charCodeAt(0);
        if (ch >= '1' && ch <= '9') {
            code = '①'.charCodeAt(0) + code - '1'.charCodeAt(0);
        } else if (ch >= 'A' && ch <= 'Z') {
            code = 'Ⓐ'.charCodeAt(0) + code - 'A'.charCodeAt(0);
        } else if (ch >= 'a' && ch <= 'z') {
            code = 'ⓐ'.charCodeAt(0) + code - 'a'.charCodeAt(0);
        }
        return String.fromCharCode(code);
    }

    /**
     * 将[1-20]数字转换为带圈的字符
     *
     * @example
     * ```typescript
     * charUtil.toCloseByNumber(1)  // '①'
     * charUtil.toCloseByNumber(12) // '⑫'
     * charUtil.toCloseByNumber(20) // '⑳'
     * ```
     *
     * @param number 被转换的数字
     * @return 转换后的字符
     * @throws Error 当数字不在1-20范围内时抛出错误
     */
    static toCloseByNumber(number: number): string {
        if (number < 1 || number > 20) {
            throw new Error('Number must be between 1 and 20');
        }
        return String.fromCharCode('①'.charCodeAt(0) + number - 1);
    }
} 